﻿// Fill out your copyright notice in the Description page of Project Settings.


#include "Subsystem/GameVersionSubsystem.h"

#include "IPlatformFilePak.h"
#include "Libraries/FileLibrary.h"
#include "Serialization/JsonSerializer.h"

void UGameVersionSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
	Super::Initialize(Collection);
	Collection.InitializeDependency(UProtocolSubsystem::StaticClass());
	// 该Subsystem依赖于UProtocolSubsystem，所以必须是在UProtocolSubsystem实例化后才能赋值
	ProtocolSubsystem = ((FSubsystemCollection<UGameInstanceSubsystem>&)Collection).GetSubsystem<UProtocolSubsystem>(UProtocolSubsystem::StaticClass());
	check(ProtocolSubsystem);
	ServerVersionPath = "http://192.168.1.71/test/ServerVersion.txt";
	ServerPakPath = "http://192.168.1.71/test/ServerPak/";
	ClientVersionPath = FPaths::ProjectSavedDir() + "test/ClientVersion.txt";
	ClientPakPath = FPaths::ProjectSavedDir() + "test/ClientPak/";
}

void UGameVersionSubsystem::UpdateVersion(FString PakPath)
{
	ClientPakPath = PakPath.EndsWith("/") ? PakPath : PakPath + "/";
	GetServerVersionContent();
}

FString UGameVersionSubsystem::ParseUrl(const FString& Content)
{
	TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(Content);
	TSharedPtr<FJsonObject> Object;
	bool bSuccessful = FJsonSerializer::Deserialize(Reader, Object);
	FString Result = "";
	if (bSuccessful)
	{
		Object->TryGetStringField(TEXT("url"), Result);
	}
	return Result;
}

TMap<FString, FString> UGameVersionSubsystem::ParseServerExistPatch(const FString& Content)
{
	TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(Content);
	TSharedPtr<FJsonObject> Object;
	bool bSuccessful = FJsonSerializer::Deserialize(Reader, Object);
	TMap<FString, FString> Result;
	if (bSuccessful)
	{
		for (auto Elem : Object->Values)
		{
			if (Elem.Key.Equals("suportVersion") || Elem.Key.Equals("url")) continue;
			const TSharedPtr<FJsonObject>* ObjectValue;
			if (Object->TryGetObjectField(Elem.Key, ObjectValue))
			{
				for (auto ObjectKey : ObjectValue->Get()->Values)
				{
					Result.Add(ObjectKey.Key, ObjectKey.Value->AsString());
				}
			}
		}
	}
	return Result;
}

TArray<FString> UGameVersionSubsystem::ParseExistVersion(const FString& Content)
{
	TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(Content);
	TSharedPtr<FJsonObject> Object;
	bool bSuccessful = FJsonSerializer::Deserialize(Reader, Object);
	TArray<FString> Result;
	if (bSuccessful)
	{
		for (auto Elem : Object->Values)
		{
			if (Elem.Key.Equals("suportVersion") || Elem.Key.Equals("url")) continue;
			Result.Push(Elem.Key);
		}
	}
	return Result;
}

TArray<FString> UGameVersionSubsystem::ParseSuportVersion(const FString& Content)
{
	TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(Content);
	TSharedPtr<FJsonObject> Object;
	bool bSuccessful = FJsonSerializer::Deserialize(Reader, Object);
	TArray<FString> Result;
	if (bSuccessful)
	{
		const TArray<TSharedPtr<FJsonValue>>* Array;
		if (Object->TryGetArrayField(TEXT("suportVersion"), Array))
		{
			for (const auto& Elem : *Array)
			{
				Result.Push(Elem->AsString());
			}
		}
	}
	return Result;
}

TMap<FString, FString> UGameVersionSubsystem::ParseClientExistPatch(const FString& Content)
{
	TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(Content);
	TSharedPtr<FJsonObject> Object;
	bool bSuccessful = FJsonSerializer::Deserialize(Reader, Object);
	TMap<FString, FString> Result;
	if (bSuccessful)
	{
		for (auto Elem : Object->Values)
		{
			Result.Add(Elem.Key, Elem.Value->AsString());
		}
	}
	return Result;
}

void UGameVersionSubsystem::GetServerVersionContent()
{
	ProtocolSubsystem->HTTPGetMessage(ServerVersionPath);
	ProtocolSubsystem->OnGetReady().AddLambda([this](FString Response)
	{
		if (Response.IsEmpty()) return;
		this->ServerVersionInfo.VersionContent = Response;
		this->ServerVersionInfo.url = ParseUrl(this->ServerVersionInfo.VersionContent);
		this->ServerVersionInfo.SuportVersion = ParseSuportVersion(this->ServerVersionInfo.VersionContent);
		this->ServerVersionInfo.PatchList = ParseServerExistPatch(this->ServerVersionInfo.VersionContent);
		this->ServerVersionInfo.VersionList = ParseExistVersion(this->ServerVersionInfo.VersionContent);
		this->GetClientVersionContent();
		this->CompareVersion();
		this->DeletePakFile();
		this->DownloadPakFile(NeedDownFiles);
	});
}

void UGameVersionSubsystem::GetClientVersionContent()
{
	FString TxtStream;
	if (!FPlatformFileManager::Get().GetPlatformFile().FileExists(*ClientVersionPath)) return;
	FFileHelper::LoadFileToString(TxtStream, *ClientVersionPath);
	if (TxtStream.IsEmpty()) return;
	ClientVersionInfo.PatchList = ParseClientExistPatch(TxtStream);
}

void UGameVersionSubsystem::CompareVersion()
{
	for (auto Elem : ServerVersionInfo.PatchList)
	{
		if (ClientVersionInfo.PatchList.Contains(Elem.Key) && ClientVersionInfo.PatchList[Elem.Key] == Elem.Value) continue;
		NeedDownFiles.Push(Elem.Key);
	}
	for (auto Elem : ClientVersionInfo.PatchList)
	{
		if (ServerVersionInfo.PatchList.Contains(Elem.Key) && !FPlatformFileManager::Get().GetPlatformFile().FileExists(*ClientVersionPath))
		{
			NeedDownFiles.Push(Elem.Key);
		} else if (!ServerVersionInfo.PatchList.Contains(Elem.Key))
		{
			NeedDelFiles.Push(Elem.Key);
		}
	}
}

void UGameVersionSubsystem::OnPakDownloadingProcessDelegate(const int32 BytesSent, const int32 BytesReceived, const int32 FullSize)
{
	PakDownloadingProcess.Broadcast(BytesSent, BytesReceived, FullSize);
}

void UGameVersionSubsystem::OnOnePakDownLoaded(TEnumAsByte<DownloadResult> Result)
{
	switch (Result)
	{
	case DownloadResult::SuccessDownloading:
		PakOneDownloadComplete.Broadcast(Result);
		DownloadPakFile(NeedDownloadPakNames, CurrentIndex + 1);
		break;
	default:
		PakAllDownloadComplete.Broadcast(Result);
	}
}

void UGameVersionSubsystem::OnAllPakDownLoaded(TEnumAsByte<DownloadResult> Result)
{
	UpdateClientVersionContent();
	// MountPak();
	PakAllDownloadComplete.Broadcast(Result);
}

void UGameVersionSubsystem::UpdateClientVersionContent()
{
	FString JsonOutString;
	TSharedRef<TJsonWriter<TCHAR, TCondensedJsonPrintPolicy<TCHAR>>> Writer = TJsonWriterFactory<TCHAR, TCondensedJsonPrintPolicy<TCHAR>>::Create(&JsonOutString);
	Writer->WriteObjectStart();
	for (auto Elem : ServerVersionInfo.PatchList)
	{
		Writer->WriteValue(Elem.Key, Elem.Value);
	}
	Writer->WriteObjectEnd();
	Writer->Close();
	FFileHelper::SaveStringToFile(JsonOutString, *ClientVersionPath);
}

void UGameVersionSubsystem::DownloadPakFile(TArray<FString> FilePath, int32 currentIndex)
{
	NeedDownloadPakNames = FilePath;
	CurrentIndex = currentIndex;
	if (!NeedDownloadPakNames.IsValidIndex(CurrentIndex))
	{
		OnAllPakDownLoaded(DownloadResult::SuccessDownloading);
		return;
	}
	ProtocolSubsystem->OnFileDownloadProgress().RemoveAll(this);
	ProtocolSubsystem->OnFileDownloadRead().RemoveAll(this);
	ProtocolSubsystem->OnFileDownloadProgress().AddUObject(this, &UGameVersionSubsystem::OnPakDownloadingProcessDelegate);
	ProtocolSubsystem->OnFileDownloadRead().AddUObject(this, &UGameVersionSubsystem::OnOnePakDownLoaded);
	ProtocolSubsystem->DownloadFile(ServerPakPath + NeedDownloadPakNames[CurrentIndex], ClientPakPath + NeedDownloadPakNames[CurrentIndex]);
}

void UGameVersionSubsystem::DeletePakFile()
{
	for (auto Elem : NeedDelFiles)
	{
		UFileLibrary::FileDelete(ClientPakPath + Elem);
	}
}

void UGameVersionSubsystem::MountPak(TArray<FString> PakPaths)
{
	TArray<FString> Result;
	if (PakPaths.Num() == 0)
	{
		ServerVersionInfo.PatchList.GenerateKeyArray(PakPaths);
	}
	for (auto Elem : PakPaths)
	{
		if (UFileLibrary::FileExist(ClientPakPath + Elem))
		{
			Result.Push(ClientPakPath + Elem);
		}
	}
	FPakPlatformFile* pakFile = (FPakPlatformFile*)(FPlatformFileManager::Get().FindPlatformFile(FPakPlatformFile::GetTypeName()));
	if (Result.Num() > 0 && pakFile)
	{
		for (FString Elem : Result)
		{
			pakFile->Mount(*Elem, 10);
		}
	}
}
